perm filename 106A02[1,RWF] blob sn#751355 filedate 1984-04-16 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00012 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	This is an algorithm (though fuzzy around the edges) for designing a program
C00009 00003	←Reading Programs←
C00010 00004	Conditional Commands
C00011 00005	*****     *****
C00013 00006	Again, we change the WRITE('B') to
C00016 00007	If we change the character constants 'A', 'D', and 'B' to asterisks, and 
C00019 00008	←OUTPUT and output.←
C00021 00009	Output Format
C00024 00010			The Most Unforgettable Characters I Have Ever Met
C00033 00011	Do-It-Yourself Input/Output
C00040 00012		Cleveland Budgets Left a Mess By One Small Computer Goof
C00048 ENDMK
C⊗;
This is an algorithm (though fuzzy around the edges) for designing a program
using the TOPS 20 system and the PASGO compiler at LOTS.

(1)  Design an algorithm for the problem.
(2)  Express the algorithm as a program in Pascal.  Assume that it contains
     errors.  (It does.)
(3)  Create a file containing the Pascal program.  Give it a name consisting of
     up to six letters followed by ".PGO"; the part before the period can be the
     problem number of an assignment, like or "P08" or an abbreviated description
     description, like "DRAWEX".  Let's say the name is "FFF.PGO."  Use
     @CREATE FFF.PGO↓.
(4)  Print a hard (paper) copy of the program, by @ PRINT FFF.PGO↓.
(5)  Desk-check the hard copy.
(6)  Prepare an input data file if needed.  Call it "FFF.DAT".
(7)  Try the program out by the TOPS-20 command
	@ EXECUTE FFF.PGO↓
(8)  If there are syntax error messages from the compiler, determine the reasons
     for them (at least the first one; sometimes a single error initiates a long
     sequence of largely unintelligible subsequent messages, if the compiler makes
     the wrong choices in recovering from the first error.)  Repair them
     (use @ EDIT FFF.PGO↓) and return to step (7).
(9)  If there are no syntax errors, as evidenced by the appearance of something like
     "EXECUTION STARTING" on the terminal screen, the program wll begin by asking
     you for the TOPS-20 names of any files (eg. INPUT and OUTPUT) your Pascal
     program uses.  The usual names are "FFF.DAT↓" and "FFF.OUT↓" respectively,
     for a program called FFF.PGO.
(10) If the program fails to run to completion, you will get an error message.
     See     for a list of error messages and their interpretation.  Such an
     error during execution means, ordinarily, that you have applied an operation
     to an argument that lies outside the correct range, like SQRT(-3.7).  Locate
     and correct the error (@EDIT FFF.PGO↓) and return to step 7.  If the program
     has not stopped in one minute, it is probably trying to run forever, or for
     several thousand years.  Stop the program with CONTROL-C, locate the
     non-terminating iteration, correct it, and return to step 7.
(11) If the program runs to completion, it will return control to the executive,
     displaying the @ on the terminal.  Now the results are on the file "FFF.OUT".
(12) Inspect your results on the terminal, if you think they will fit on the
     screen, by @TYPE FFF.OUT↓.  Print them and the program with 
     @PRINT FFF.OUT,FFF.PGO↓, and get them from the printer.  Since LOTS is a
     self-service system, you are as responsible as anyone else for keeping the
     printer clear of paper jams and supplied with paper, ribbons, and tender
     loving care; inform yourself about these things ASAP.  If the results are
     unbelievable, find and correct the logical errors in your program and return
     to step 7.
(13) You are finished.  Release the terminal for someone else to use, by
     @LOGOUT↓.  This action is very much in your interest as well as in the
     interest of anyone waiting.  Failing to do so is like leaving your headlights
     on when you park your car, with the keys in the ignition.  Whenever you don't
     know what you are doing, your best practice to conserve your time allocation
     is to log out and spend time desk checking.
←Reading Programs←

No textbook can hint at all the methods programmers use in their programs.
We recommend that you read every Pascal program that you can legitimately
lay your hands on.  Sources include other textbooks (watch out, though:
many contain erors, unlike this one), class handouts of this and other
courses, and the LOTS directory <CS10X>.  You might ask more experienced
programmers if you can read some of their programs, but please don't browse
without asking.

Conditional Commands

If you really loved me you'd lose weight.

If you really loved me you wouldn't mind my weight.

If wishes were horses, beggars would ride.

If there is no God, then everything is permitted.

If we had ham, we'd have ham and eggs, if we had eggs.
*****     *****
******    *****
*******   *****
********  *****
********* *****
***************
***** *********
*****  ********
*****   *******
*****    ******
*****     *****

How can we design a program to print the letter "N" as shown above?  First, let's
print out a block of A's the same size as the N.  (Later we will change the program
so that some of the A's become *'s and some become blank spaces).

	FOR R:= 1 TO 11 DO
		BEGIN
		FOR C:= 1 TO 15 DO
			WRITE('A');
		WRITELN
		END

AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA

Now we change the program so that everything in columns 11 to 30 is a B, rather
than an A.  We do this by changing WRITE('A') to

IF C<= 5 THEN WRITE('A') ELSE WRITE('B')

AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
Again, we change the WRITE('B') to

IF C>= 11 THEN WRITE('B') ELSE WRITE('C')

AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB

The whole program is now
	FOR R:= 1 TO 11 DO
		BEGIN
		FOR C:= 1 TO 15 DO
			IF C<= 5 THEN WRITE('A')
			ELSE IF C>= 11 THEN WRITE ('B')
			ELSE WRITE('C')
		WRITELN
		END

Now we change WRITE('C') to

IF C>= 5+R THEN WRITE('C') ELSE WRITE('D')

AAAAACCCCCBBBBB
AAAAADCCCCBBBBB
AAAAADDCCCBBBBB
AAAAADDDCCBBBBB
AAAAADDDDCBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB

and finally change WRITE('D') to

IF C>= R THEN WRITE('D') ELSE WRITE ('E').  

AAAAACCCCCBBBBB
AAAAADCCCCBBBBB
AAAAADDCCCBBBBB
AAAAADDDCCBBBBB
AAAAADDDDCBBBBB
AAAAADDDDDBBBBB
AAAAAEDDDDBBBBB
AAAAAEEDDDBBBBB
AAAAAEEEDDBBBBB
AAAAAEEEEDBBBBB
AAAAAEEEEEBBBBB
If we change the character constants 'A', 'D', and 'B' to asterisks, and 
'C', 'E', to blanks, the program is


PROGRAM NF(OUTPUT);
VAR R,C:INTEGER:
BEGIN
FOR R:=1 TO 11 DO
   BEGIN
   FOR C:=1 TO 15 DO
      IF C<=5 THEN WRITE('*')
      ELSE IF C>=11 THEN WRITE('*')
      ELSE IF C >=5+R THEN WRITE(' ')
      ELSE IF C>=1+R THEN WRITE('*')
      ELSE WRITE(' ');
   WRITELN
   END
END.





Exercise:  Design an algorithm to print a letter `Z'.

←OUTPUT and output.←

The results printed by your programs go into a file called OUTPUT.  This is
only a temporary name for the file, used while your program is being
executed.  When the program is finished, the file is saved in your
directory, under another name.  Reasons for this system are:

(1) You may want to execute the same program several times, and keep all
the output files; they need distinct names.

(2) You may want to execute different programs, all of which call their
result files OUTPUT, and keep all the output files; again, they need
distinct names.

(3) The system for naming files varies from one computer to another.  The
Pascal standard can't accomodate all of them.  At LOTS, for example, a file
name has three parts, separated by periods; the third part is a number.

When you execute a Pascal program at LOTS, after translating the program
into machine language the executive program will ask 
OUTPUT:
and wait for you to type the permanent name of your result file.  I
normally use the program name, followed by ".OUT".

Output Format

In a Pascal WRITE or WRITELN command, each operand is normally printed in a
standard format for the type of value it has.  If the operand is an integer
variable, and the computer uses 36 biwords, the value of the variable is
printed as a decimal number of up to 11 digits, possibly preceded by a minus
sign.  Including a blank space to separate adjacent numbers, we use a
13-character field, with the first character always blank, and the value of the
number right-justified so that it is easy to print columns of numbers with the
units digits lined up vertically.

If you know, however, that the value of variable I is positive, and no more than
(say) 4096, you may want to print it in four or five character regions, to save 
space.

This can be done by using I:4 or I:5 as the operand of a WRITE or WRITELN command.
Any expression appearing as an operand may be followed by a colon and the desired
length of field.  Usually the length is a constant, but any expression with an
integer value will do.

If the value won't fit in a field of the specified size, Pascal embodies the
assumption that the user prefers to get full information at the expense of
consistent appearance; the field is expanded to fit the operand.  This can be
useful when you want to print numbers in the context of natural language; the
command WRITE('there are', I:1 'airplanes approaching') can result in

	There are 2 airplanes approaching
or	There are 2000000 airplanes approaching
		The Most Unforgettable Characters I Have Ever Met



Pascal works with other types of data besides numbers and truth values.  One
such type is the set of characters found on the typical computer terminal
keyboard.  The set includes the digits 0-9, the letters a-z, A-Z in both cases
(minuscule and majuscule, if you want the precise names), all the familiar
punctuation marks, all the mathematical symbols used in Pascal, the space, and
a few other odds and ends like $ and @.

This type is called CHAR (which may as well be pronounced ``character'') in
Pascal.  Like any fully equipped modern type, it has its own citizenry of
constants and variables, and an intra- and inter-type transportation system
of operators and functions, which can be combined in expressions for more
elaborate tours.

For the set of possible values of type CHAR, see Table  C1, pg 440 of SW&P.
There is a literal constant for each one.  With one exception, the literal
constant is the character itself, preceded and followed by apostrophes 
('A', '2', ':').  The exception is the apostrophe itself; its constant
form is four apostrophes in a row ('''').

Symbolic constants can be defined in terms of the literal constants:

	CONST LASTVOWEL='u';

Variables are of type CHAR if declared so; they may be assigned any values of
of that type.  Here is a fragmentary program using only characters:

	PROGRAM...
	CONST COLON=':'; MYINITIAL='F';
	VAR C,H : CHAR;
		BEGIN					
		C:='A';
		H:=MYINITIAL;
		WRITE('B',COLON,H,C)
		C:=H
		WRITELN(C)
		END (* output is B:FAF *)

Characters can be read to character variables from the INPUT file, or from
any text file or CHAR file.  If

	READ(C,H)

appeared in the above program, the next two characters of the INPUT file
would be read and assigned to C and H.  At the end of a line, READ(C)
would read the end-of-line symbol (which is not a character in the 
sense of belonging to CHAR) and assign a space to C.

The characters of CHAR have a conventional ordering on each computer, which
roughly corresponds to alphabetical order.  There is also a conventional
numbering, giving each character the number of its place in the ordering.
We can use the operators <,>,=, and their combinations to test the relative
ordering of two characters.  Standard Pascal requires that the capital letters
A-Z be in alphabetical order, and that the lower case letters be in alphabetical
order, (other characters may be interspersed!) To find out if the value of C,
already known to be a letter, is one of 'I','J','K','L','M', or 'N' in either
case, we could use

	IF (C>='I')AND(C<='N')OR(C>='i')AND(C<='n').

Standard Pascal also requires that the digits 0-9, as characters, be in numerical
order and consecutive.

Here is program fragment which expects the word YES or NO on the INPUT file,
possibly preceded by spaces and abbreviated.  It will read the whole word,
and choose between commands SY and SN.

	READ(C);
	WHILE C=' ' DO READ(C);
	READ(H);
	WHILE H<> ' ' DO READ(H);
	IF C='Y' THEN SY
	ELSE IF C='N' THEN SN
	ELSE WRITE('INPUT OTHER THAN Y,N')

Standard functions with characters as arguments or as values include these:

	ORD(C) is the ordinal number of the character C.
		Tables in Appendix C of SW&P give the
		most common ordinal numberings of 
		characters; you should find out which 
		is used on your computer.  
		In the ASCII ordering, used at LOTS, 
		letters are consecutive.

	CHR(N)  is the character (if any) whose ordinal
		number is N.  It is the inverse of ORD;
		CHR(ORD(C))=C.

	PRED(C) is the immediate predecessor of C in the
		ordering; it is the same as CHR(ORD(C)-1).
		If C is the first character in the ordering,
		it is a mistake to use PRED(C).

	SUCC(C) is the immediate successor of C in the ordering;
		it is the same as CHR(ORD(C)+1).  If C is the
		last character in the ordering, it is a mistake
		to use SUCC(C).

To print the upper case alphabet using ASCII, we can use either of the two 
program fragments


(1)	C:='A';
	WHILE C<='Z' DO
		BEGIN
		WRITE(C)
		C:=SUCC(C)
		END

(2)	FOR C:='A' TO 'Z' DO
		WRITE(C)

The second program works because iteration variables may be of any type that
has an ordinal numbering, in particular of type CHAR.

We shall later encounter types which have as their values whole sequences of
characters, including words, sentences, formulas, etc; such sequences we call
strings.  We have already encountered writing of string constants, consisting
of two or more characters enclosed in single quotes (apostrophes).  For the
moment, the reader may treat writing of a string constant as an abbreviation
for writing the characters of the constant;

	WRITE('ABC') abbreviates WRITE('A','B','C').

What can be done with characters?  Only a limited amount, until we can work
with long strings of them.  As an example above showed, they can be used as
convenient input codes to choose between actions.  In output, strings of
characters provide titles, punctuation, etc.  Within a computation, however,
characters are only a convenience; rather than work with characters, a program
could work with their ordinal numbers, so we can not expect to do anything with
characters we could not do without them.



	Exercise: Write an iterative program to print the consonants.

	Exercise: Write a program to read a roman numeral and print its
		value in decimal.
		(Hint: the value of letter is negative if the next
		letter has a larger value.)

	Examples of roman numerals: III = 3, VI = 6, IX = 9, LIV = 54, 
				    XCIX = 99, MCMLXXXIII = 1983, D = 500.


	Exercise: Write a program to count to M in roman numerals.

Do-It-Yourself Input/Output

Reading and printing of decimal numbers is not a primitive operation on most
computers, so Pascal provides standard subalgorithms to handle most of the
common situations that arise.  Now and then, however, a problem comes up
where the built-in mechanisms are inadequate, and the programmer must
explicitly handle the individual decimal digits of numbers moving to or
from files.  Suppose we have a number N, known to be greater than 100,000
and less than 1,000,000, which we want to print punctuated with a comma
after the thousands digit, like `123,456'.  The WRITE command for numbers
does not provide for this form, so the program will have to execute
WRITE(','), preceded by something that writes out (say) 123, and followed by
something that writes 456.

We can calculate the numbers to be printed before and after the comma as N
DIV 1000 and N mod 1000 repectively, and if N is 123456, the command
WRITE(N DIV 1000:3,',',N MOD 1000:3) prints

		123,456

as desired.  If N is 123004, however, we get an unpleasant surprise:

		123,  4

because the WRITE command never prints leading zeroes.  For the part of the
number that follows the comma, at least, we must work out the individual
digits, and print them separately.  To get the tens digit requires two
steps.  Taking N MOD 100 gives us the numerical value of the two right hand
digits (56 and 4 in the examples above), after which a division by 10 gives
the tens digit.  To print a six digit integer N withut supressing leading
zeroes can be done with this subprogram:

	D:=1000000;
	FOR I:= 1 TO 6 DO
		BEGIN
		D:=D DIV 10;	(*FIRST TIME 100000, LAST TIME 1*)
		Q:=N DIV D;	(*NEXT DIGIT OF N*)
		WRITE(Q:1);
		N:=N MOD D
		END

where  a comma can be inserted by inserting IF I=3 THEN WRITE (',') after
the WRITE command.

Exercise

If we don't know that N is greater than 100,000, a more complicated
algorithm is needed.  Write a program that will print N in one of these
forms:

1,234,567
123,456
12,345
1,234
123
12
1
0

(Solution:  make 0 a special case, otherwise remember if there has been a
non-zero digit, supressing all printing until a non-zero digit has been
encountered.)

Suppose we want to read integers containing commas, ignoring the commas,
and stopping at the first blank space.  Reading into an integer variable
can't handle the task, so we have to read individual characters, building
up the value of the number from the values of the indivdiual characters.
While the ordinal values of the digit characters are not the same as their
numerical values (this is because the digits are not the first characters
in standard alphabetical order), they are consecutive, so we can read a
digit into a CHAR variable and get its numerical value DIGIT by 
	READ (C);
	DIGIT:= ORD(C) - ORD('O').

We get the value of a multi-digit member like 123456 from the values of its
individual digits by treating it as a polynomial

	1 X 10↑5 + 2 X 10↑4 + 3 X 10↑3 + 4 X 10↑2 + 5 X 10 + 6,
or more simply
	((((1 X 10 + 2) X 10 + 3) X 10 + 40 X 10+ 5) X 10 + 6.

Now the plan of the algorithm becomes clear.  We read the characters of the
number, discarding commas, and keeping track of the numerical value of the
part of the number so far seen:

	N:=0;
	READ(C);
	WHILE C <> ` ' DO
		BEGIN
		IF C <> `,' THEN
			N:=N * 10 + ORD(C) - ORD('O');
		READ(C);
		END;

	(*N CONTAINS THE NUMBER READ*)

This program is not fussy about what it reads.  If the first input character is
blank, it treats that as a way of writing 0.  If the input string is ,,35,0,
it treats that as a way of writing 350.  A better program would watch for such
inputs as possible errors.


_Exercise_

The subprogram above fails if the number read is close to the maximum integer
value MAXINT.  Revise it so it can read any positive integer up to MAXINT.

_Exercise_

Revise it to detect an attempt to read a number larger than MAXINT.  Why is
	IF N > MAXINT THEN WRITE ('TOO BIG')
not a good approach?

_Exercise_

Revise the reading algorithm to read integers written in base 16, with the
letters A through F serving as digits with values 10 through 15.

	Cleveland Budgets Left a Mess By One Small Computer Goof

	Cleveland

	Someone in the county auditor's office listed the worth of
	a home  owned by  Joseph and  Elizabeth Sprenger  at  $510
	million.  The valuation plunged the city's budget and  the
	schools' budget into confusion.

	The house was worth $35,000.

	As a result, Cleveland  will get $2  million less in  real
	estate revenues than expected and the city's schools  will
	get $4  million less.   Both had  based their  budgets  on
	anticipated revenues that included the auditor's error.

	``It throws our whole budget  out of whack,'' said  Joseph
	Tegreene, a member of the  school board.  ``We were  proud
	we had produced  the first balanced  budget in five  years
	without a state loan.''

	It was not known who made the error.

	Cuyahoga County Auditor  Tim McCormack said  someone at  a
	computer console apparently placed the `5100' code  number
	used to indicate  residential homes  at the  front of  the
	value  for  the  Sprengers'  home.   The  value  came  out
	$510,035,000.

	The Sprengers never got a tax bill on the erroneous value.
	It was corrected before being sent out, but McCormack said
	no one notified the schools.

	Tegreene said he expects the  school board to try to  trim
	$4 million from  its budget.  Phillip  Allen, city  budget
	director, said the  city will  do new  projections on  its
	budget.

			 ---San Francisco Chronicle, June 24, 1983


	
``Who's buried in downtown Cleveland?''

	``Everybody.''

			Another Cleveland Joke
			   Robert W. Floyd
			   Copyright  1983

Don't blame the typist who entered the incorrect value of the Sprenger home into
the computer.  Typists always make mistakes; a well-designed information
processing system takes that into account, and detects or allows for the mistakes.
Don't blame the computer, either.  It was just following orders, and, unlike
Adolph Eichmann, it had no choice in the matter.  I can imagine it muttering to
itself ``This don't seem right somehow.  I wish I had some way to ask somebody
about it.''  A good computer programmer always designs his programs to adequately
treat incorrect as well as correct data.  Among the options common sense would
suggest to a programmer of a property tax base evaluation:

(1) Each type of property has a certain plausible maximum and minimum value.
    The maximum plausible value for a house might be 120% of the previous year's
    highest assessed value.

(2) Such maxima and minima might be set by neighborhoods; a single-family house
    in a slum is unlikely to be worth more than $100,000, while many a house in
    Shaker Heights might top $1,000,000.

(3) If the previous year's evaluation of the same property is available on a
    computer file, plausible maxima and minima might be (say) 20% above and below 
    the old valuation.

(4) Depending on the degree of implausibility, the computer might request 
    confirmation of the input by the typist:

	``Valuation increased by 35% in one year.  Is this confirmed?
	Type Y or N.''

    It might demand credentials:

	``New house evaluated at $2,800,000.  Evaluations over
	$1,000,000 must be confirmed by county assessor or deputy.
	Please enter password of confirming officer, or the word
	POSTPONE.''

It might simply refuse to accept certain data.  

	``Valuation of single family home over $10,000,000 does not compute.  
	Don't mess with me or I'll raise your electric bill.''
	
In addition to the willingness of Cleveland's program to accept bad data, it
apparently had a second failing; the left hand did not know what the right one was 
doing.  The Sprenger assessment was corrected for the purposes of calculating the
Sprenger's tax bill, but not for the purpose of calculating the city's tax base.
Part of the discipline of data base management (the handling of large files of
repeatedly used data) is to avoid using multiple copies of what should be the
same data in different places; such multiple copies can lead to internal
inconsistency in the data base.

The use of a numerical code to indicate the type of building is an open
invitation to the kind of error that occured.  Why not use ``R,'' rather
than 5100, to mean residence?

A good programmer should always ask "What happens to the world if someone enters
bad data into my program, five years in the future, when I've gone away to the
job that finally pays me what I'm worth?  Will the program be likely to detect
bad data?  Will the results of undetected bad data seem plausible?  How likely
are bad data?  How catastropic are the consequences?''

If you ever go to work as a programmer for Cuyahoga County, please tell them
what I said.  And try to arrange to be paid by a handwritten paycheck.
(On the other hand, if you opt for a computerized paycheck, you might get one
for $510,035,000.  There's always that chance.)

``Did you hear the one about the river catching fire in Cleveland?''

	``Yes.''